利用SharedPreferences儲存資料,練習做一個類似密碼鎖的頁面
除了MainActivity外,再建立一個Password Activity的頁面
當設定密碼後,會顯示Password的頁面
須輸入正確密碼才會回到MainActivity
在MainActivity中建立一個Intent物件,用於啓動新的頁面
Intent(參數1,參數2):
val intentPass = Intent(this, PassActivity::class.java)
再呼叫startActivity並把Intent物件傳入
當按下SAVE按鍵時就是設定密碼,跳轉到須要輸入密碼的頁面
btn_save.setOnClickListener {
...
startActivity(intentPass) // intent to PassActivity
}
當輸入的密碼正確,呼叫finish()
將目前的密碼Activity結束掉,再顯示回原本的MainActivity頁面
程式碼如下
@ExperimentalStdlibApi
class MainActivity : AppCompatActivity() {
companion object {
var password = ""
var isResume = false
lateinit var intentPass: Intent
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val pref = SharedPreferences(this)
intentPass =
Intent(this, PassActivity::class.java) //.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
if (!pref.getData().isNullOrEmpty() && !isResume) {
startActivity(intentPass) // intent to PassActivity
password = pref.getData().toString()
}
tv_data.text = pref.getData()
btn_save.setOnClickListener {
if (!ed_data.text.isNullOrEmpty()) {
pref.saveData(ed_data.text.toString())
}
tv_data.text = pref.getData()
password = pref.getData().toString()
if (password != "" && password != "null") {
startActivity(intentPass) // intent to PassActivity
}
}
btn_delete.setOnClickListener {
pref.delete()
ed_data.text.clear()
tv_data.text = pref.getData()
password = ""
}
}
override fun onResume() {
super.onResume()
if (isResume && password != "" && password != "null") {
startActivity(intentPass) // intent to PassActivity
}
}
override fun onPause() {
super.onPause()
isResume = true
}
}
@ExperimentalStdlibApi
class PassActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pass)
val passwordList = mutableListOf<Int>()
tv_hint.text = MainActivity.password
btn1.setOnClickListener(::setPwdAndCheck)
btn2.setOnClickListener(::setPwdAndCheck)
btn3.setOnClickListener(::setPwdAndCheck)
btn4.setOnClickListener(::setPwdAndCheck)
btn5.setOnClickListener(::setPwdAndCheck)
btn6.setOnClickListener(::setPwdAndCheck)
btn7.setOnClickListener(::setPwdAndCheck)
btn8.setOnClickListener(::setPwdAndCheck)
btn9.setOnClickListener(::setPwdAndCheck)
btn0.setOnClickListener(::setPwdAndCheck)
btn_cancel.setOnClickListener {
if (passwordList.isNotEmpty()) passwordList.removeLast()
printOOOO(passwordList.size)
if (passwordList.size == 0) tv_password.text = "_ _ _ _"
}
}
override fun onBackPressed() {
super.onBackPressed()
finishAffinity()
}
private fun setPwdAndCheck(view: View) {
val num = (view as TextView).text.toString().toIntOrNull()?:return
if (passwordList.size < 4) passwordList.add(num)
printOOOO(passwordList.size)
finish(passwordList)
}
private fun printOOOO(int: Int) {
tv_password.text = ""
var string = ""
for (i in 1..int) {
tv_password.text = tv_password.text.toString() + "● "
string = ""
for (j in 1..(4 - int)) {
string += "_ "
}
}
tv_password.text = tv_password.text.toString() + string
}
private fun finish(list: MutableList<Int>) {
var string = ""
list.forEach {
string += it
}
if (string == MainActivity.password) {
this.finish()
MainActivity.isResume = false
}
}
}
要注意的是,當activity切換時,未顯示的畫面會被加到back stack
預設是若按系統的返回鍵,就會丟掉目前畫面,再把back stack最上層的畫面顯示出來
等於按返回鍵可以跳過不用輸入密碼,就回到Main頁面
所以在PassActivity要覆寫按返回鍵的動作,結束所有的activity
override fun onBackPressed() {
super.onBackPressed()
finishAffinity()
}
這樣在密碼頁面按下返回鍵就會退出app,而不會回到Main頁面
還有因爲code寫的實在太糙了,想要在app從背景回到前景時
偵測有設定密碼的話,也startActivity到密碼頁面
所以在onCreate(),onResume(),都加入startActivity
結果在造成實例多個密碼頁面,想要用SINGLE_TOP
Intent(this, PassActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
限制只生成一個實例來解決,不過還是有問題
查到官方有一段說明,可能是這個原因
假設任務的返回堆棧包含根Activity A以及Activity B、C和位於頂部的D(堆棧為ABCD;D位於頂部)。收到以D類型Activity為目標的intent。如果D採用默認的"standard"啟動模式,則會啟動該類的新實例,並且堆棧將變為ABCDD。但是,如果D的啟動模式為"singleTop",則D的現有實例會通過onNewIntent()接收intent,因為它位於堆棧頂部,堆棧仍為ABCD。但是,如果收到以B類型Activity為目標的intent,則會在堆棧中添加B的新實例,即使其啟動模式為"singleTop"也是如此。
嘗試改用FLAG_ACTIVITY_CLEAR_TOP就可以了
如果要啟動的Activity已經在當前任務中運行,則不會啟動該Activity的新實例,而是會銷毀位於它之上的所有其他Activity,並通過onNewIntent()將此intent傳送給它的已恢復實例(現在位於堆棧頂部)。
參考
https://developer.android.com/training/basics/firstapp/starting-activity#BuildIntent
https://developer.android.com/guide/components/activities/tasks-and-back-stack
https://developer.android.com/reference/android/app/Activity#finishAffinity()